/*
    Copyright (c) 2016, lovcreate.com. All rights reserved.
*/
package com.lovcreate.shop.service.util;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;


/**
 * 文件名 : Redis工具类
 * 创建人： 高博
 * 日期： 2016-12-20
 * 版本号： 1.0
 */
public class RedisUtil {
    protected static ReentrantLock lockPool = new ReentrantLock();

    protected static Logger logger = LoggerFactory.getLogger(RedisUtil.class);

    //Redis服务器IP
    private static String ADDR_ARRAY;

    //Redis的端口号
    private static int PORT;

    //可用连接实例的最大数目；
    //如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。
    private static int MAX_ACTIVE;

    //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例
    private static int MAX_IDLE;

    //等待可用连接的最大时间，单位毫秒，默认值为-1，表示永不超时。如果超过等待时间，则直接抛出JedisConnectionException；
    private static int MAX_WAIT;

    //超时时间
    private static int TIMEOUT;
    //auth密码
    private static String PASSWD;
    private static JedisPool jedisPool = null;

    private static Properties properties = null;

    /**
     * redis过期时间,以秒为单位
     */
    public final static int EXRP_HOUR = 60 * 60;            //一小时
    public final static int EXRP_DAY = 60 * 60 * 24;        //一天
    public final static int EXRP_MONTH = 60 * 60 * 24 * 30;//一个月
    public final static int EXRP_SECONDS = 60; //一分钟


    static {

        properties = new Properties();

        try {
            properties.load(RedisUtil.class.getClassLoader().getResourceAsStream("redis.properties"));
            MAX_ACTIVE = Integer.parseInt(properties.getProperty("MAX_ACTIVE"));
            MAX_IDLE = Integer.parseInt(properties.getProperty("MAX_IDLE"));
            MAX_WAIT = Integer.parseInt(properties.getProperty("MAX_WAIT"));
            ADDR_ARRAY = properties.getProperty("ADDR_ARRAY");
            PORT = Integer.parseInt(properties.getProperty("PORT"));
            TIMEOUT = Integer.parseInt(properties.getProperty("TIMEOUT"));
            PASSWD = properties.getProperty("PASSWD");
        } catch (IOException e) {
            logger.error("读取配置文件失败", e);
        }
        try {
            initialPool();
        } catch (Exception e) {
            logger.error("初始化redis连接池失败", e);
        }
    }


    /**
     * 初始化Redis连接池
     */
    private static void initialPool() {
        Jedis jedis = null;
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(true);
            logger.info(ReflectionToStringBuilder.toString(config));
            logger.info(ADDR_ARRAY.split(",")[0]);
            logger.info(String.valueOf(PORT));
            jedisPool = new JedisPool(config, ADDR_ARRAY.split(",")[0], PORT, TIMEOUT, PASSWD);
            jedis = jedisPool.getResource();
        } catch (Exception e) {
            logger.error("First create JedisPool error :{} ", e.getMessage(), e);
            try {
                //如果第一个IP异常，则访问第二个IP
                JedisPoolConfig config = new JedisPoolConfig();
                config.setMaxTotal(MAX_ACTIVE);
                config.setMaxIdle(MAX_IDLE);
                config.setMaxWaitMillis(MAX_WAIT);
                config.setTestOnBorrow(true);
                jedisPool = new JedisPool(config, ADDR_ARRAY.split(",")[1], PORT, TIMEOUT, PASSWD);
                jedis = jedisPool.getResource();

            } catch (Exception e2) {
                logger.error("Second create JedisPool error :{} ", e2.getMessage(), e);
                e2.printStackTrace();
            }

        } finally {
            returnResource(jedis);
        }
    }


    /**
     * 在多线程环境同步初始化
     */
    private static void poolInit() {
        //断言 ，当前锁是否已经锁住，如果锁住了，就啥也不干，没锁的话就执行下面步骤
//        assert !lockPool.isHeldByCurrentThread();
        lockPool.lock();
        try {
            if (jedisPool == null) {
                initialPool();
            }
        } catch (Exception e) {
            logger.error("poolInit throw Exception:{}", e.getMessage(), e);
            e.printStackTrace();
        } finally {
            lockPool.unlock();
        }
    }


    public static Jedis getJedis() {
        //断言 ，当前锁是否已经锁住，如果锁住了，就啥也不干，没锁的话就执行下面步骤
//        assert !lockJedis.isHeldByCurrentThread();
//        lockJedis.lock();
        Jedis jedis = null;
        if (jedisPool == null) {
            try {
                poolInit();
            } catch (Exception e) {
                logger.info("redis连接池初始化失败", e);
                return jedis;
            }
        }

        try {
            jedis = jedisPool.getResource();
        } catch (Exception e) {
            logger.error("获取jedis实例失败: {}", e.getMessage(), e);
            return null;
        }
        return jedis;
    }


    /**
     * 释放jedis资源
     *
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        if (jedis != null && jedisPool != null) {
            jedisPool.returnResourceObject(jedis);
        }
    }


    /**
     * 设置 String
     *
     * @param key
     * @param value
     */
    public static void setString(String key, String value) {
        Jedis jedis = null;
        try {
            value = StringUtils.isEmpty(value) ? StringUtils.EMPTY : value;
            jedis = getJedis();
            jedis.set(key, value);
        } catch (Exception e) {
            logger.error("Set key error : {}", e.getMessage(), e);

        } finally {
            if (null != jedis) {
                jedisPool.returnResourceObject(jedis);
            }
        }
    }

    /**
     * 设置 过期时间
     *
     * @param key
     * @param seconds 以秒为单位
     * @param value
     */
    public static void setString(String key, String value, int seconds) {
        logger.info("key:{}, value:{}", key, value);
        Jedis jedis = null;
        try {
            value = StringUtils.isEmpty(value) ? "" : value;
            jedis = getJedis();
            jedis.setex(key, seconds, value);
        } catch (Exception e) {
            logger.error("redis写入错误 : {}", e.getMessage(), e);

        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 获取String值
     *
     * @param key
     * @return value
     */
    public static String getString(String key) {
        if (StringUtils.isBlank(key)) {
            return null;
        }
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.get(key);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key, e);
            return null;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 头部插入
     *
     * @param key
     * @param value
     */
    public static void listPush(String key, String value) {
        if (StringUtils.isBlank(key)) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.lpush(key, value);
        } catch (Exception e) {
            logger.error("redis列表添加失败, key={}", key);
            throw e;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 头部批量插入
     *
     * @param key
     * @param value
     */
    public static void listPush(String key, List<String> value) {
        if (StringUtils.isBlank(key) || CollectionUtils.isEmpty(value)) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            for (int i = value.size() - 1; i > -1; --i) {
                jedis.lpush(key, value.get(i));
            }
        } catch (Exception e) {
            logger.error("redis列表添加失败, key={}", key);
            throw e;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    public static List<String> listPageQuery(String key, int offset, int pageSize) {
        if (StringUtils.isBlank(key)) {
            return Collections.EMPTY_LIST;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.lrange(key, offset, offset + pageSize);
        } catch (Exception e) {
            logger.error("redis列表添加失败, key={}", key);
            throw e;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 列表删除某个元素
     *
     * @param key
     * @param value
     * @return
     */
    public static boolean listDel(String key, String value) {
        if (StringUtils.isBlank(key)) {
            return false;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.lrem(key, 1, value) > 0;
        } catch (Exception e) {
            logger.error("redis列表删除失败, key={}", key);
            throw e;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 列表获取
     *
     * @param key
     */
    public static List<String> listQuery(String key) {
        if (StringUtils.isBlank(key)) {
            return null;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.lrange(key, 0, -1);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key, e);
            return null;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    public static void invalidateKey(String key) {
        if (StringUtils.isBlank(key)) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } catch (Exception e) {
            logger.error("失效key失败, key={}", key);
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    public static void delKey(String key) {
        if (StringUtils.isBlank(key)) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key);
            return;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 集合添加
     *
     * @param key
     */
    public static void setAdd(String key, String value) {
        if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.sadd(key, value);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key);
            return;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 集合删除
     *
     * @param key
     */
    public static void setDel(String key, String value) {
        if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.srem(key, value);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key);
            return;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 集合获取
     *
     * @param key
     */
    public static Set<String> setQuery(String key) {
        if (StringUtils.isBlank(key)) {
            return null;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.smembers(key);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key);
            return null;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 集合获取
     *
     * @param key
     */
    public static boolean setContains(String key, String value) {
        if (StringUtils.isBlank(key)) {
            return false;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.sismember(key, value);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key);
            return false;
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * 设置 过期时间
     *
     * @param key
     * @param seconds 以秒为单位
     * @param value
     */
    public static void saveBytes(byte[] key, byte[] value, int seconds) {
        Jedis jedis = null;
        try {
            if (key == null) {
                return;
            }
            jedis = getJedis();
            jedis.setex(key, seconds, value);
//            String code = VerifyCodeGenerator.generator(5, false);
//            logger.info("code:{}", code);
//            jedis.setex(code, seconds, "ZT");
        } catch (Exception e) {
            logger.error("redis写入错误 : {}", e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    /**
     * byte[] 查询
     *
     * @param key
     */
    public static byte[] getBytes(byte[] key) {
        Jedis jedis = null;
        try {
            if (key == null) {
                return null;
            }
            jedis = getJedis();
            return jedis.get(key);
        } catch (Exception e) {
            logger.error("redis写入错误 : {}", e.getMessage(), e);
            e.printStackTrace();
            return null;
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    public static void updateExpire(byte[] key, int expire) {
        Jedis jedis = null;
        try {
            if (key == null) {
                return;
            }
            jedis = getJedis();
            jedis.expire(key, expire);
        } catch (Exception e) {
            logger.error("redis更新失效时间错误 : {}", e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (null != jedis) {
                jedisPool.returnResource(jedis);
            }
        }
    }

    public static void delByteKey(byte[] key) {
        if (null == key) {
            return;
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } catch (Exception e) {
            logger.error("查询key失败, key={}", key);

        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
    }


    public static void main(String[] args) {
        // RedisUtil.setString("test", "gaobo", 20);
        System.out.println(RedisUtil.getString("5eb2ef37f3db4295a194c1db95e7783d"));
    }
}
